	title	'Z-80 Floppy Disk Test'
; Floppy Disk Test for Z-80 CP/M Systems
;
; version 1.0  16 December 1980
;
; The purchaser may freely create any number
; of copies of this program on magnetic media
; as necessary to support his computer system(s).
; Further resale of this program is prohibited.
;
; Copyright (c) 1980 by
; Ray Duncan
; Laboratory Microsystems
; 4147 Beethoven Street
; Los Angeles, CA 90066
;
	org	100h
;
cpm	equ	5	;references to
wboot	equ	0 	;operating system
;
;			references to ASCII char
cr	equ	0dh
lf	equ	0ah
ff	equ	0ch
tab	equ	09h
;
;			parameters for disk
;			(supplied as single
;			density, soft sectored)
$drvf	equ	1	;first drive to allow
			;testing (0=A,1=B,etc)
$drvl	equ	3	;last drive to allow
			;testing
$trkf	equ	0	;first track
$trkl	equ	76	;last track
$secf	equ	1	;first sector
$secl	equ	26	;last sector
$bps	equ	128	;bytes per sector
$bpt	equ	$bps*$secl ;bytes per track
;
			;number of digits to
			;accept in track and
			;sector assignments
$dig	equ	2	;(should be set larger
			; for devices having 
			; track or sector
			; numbers >99 )
;
;			program identification
$ver	equ	1	;version
$rev	equ	0	;revision
;
	jp	dtst	;enter from CP/M
	form
; global variables for use by all routines
;
pass	dw	0	;current pass number
errors	dw	0	;error count for pass
;
			;the following variables
			;are used by RDBUF and
			;WTBUF to address the
			;disk, and by PERR to
			;display failing disk
			;addresses ---
drv	db	0	;drive to test
trk	dw	0	;current track
sec	dw	0	;current sector
buffer	dw	0	;current memory address
iolen	dw	0	;bytes last transferred
;
			;the following variables
			;define the area to be
			;tested on the selected
			;disk drive ---
trkf	dw	0	;first track to test
trkl	dw	0	;last track to test
secf	dw	0	;first sector to test
secl	dw	0	;last sector to test
;
			;the following variables
			;define the test mode ---
			;
bypass	db	0	;0=do not bypass error
			;itemization, 1=bypass
			;error itemization,print
			;total errors per pass
			;only.
			;
skew	db	0	;0=no sector skew
			;1=use sector skew for
			;  increased test speed
			;
list	db	0	;0=print errors on
			;terminal, 1=print errors
			;on list device.
			;
lockio	db	0	;0=no lock
			;1=lock on read
			;2=lock on write
			;
restor	db	0	;0=do not restore original
			;data, 1=restore original
			;data on diskette
			;
lockpt	db	0	;0=use variable test
			;data pattern, 1=lock on
			;user supplied data pattern
			;
pattrn	db	0	;contains user supplied
			;8 bit data pattern
			;
passl	dw	0	;last pass to do on this
			;test run
			;
digits	db	$dig	;maximum number of digits
			;to be accepted during
			;decimal or hexadecimal
			;numeric input.
			;
xtran	dw	sectrb	;address of sector
			;translation table
	form
;
; disk test --- main control
;
dtst	equ	$	;entry from CP/M
	ld	de,dtsta;print program title
	ld	c,9
	call	cpm
	ld	hl,(cpm+1)
	ld	de,buffend
	or	a,a	;make sure enough user
	sbc	hl,de	;memory to execute test
	jr	nc,dtst01
	ld	de,dtsts;not enough memory,
	ld	c,9	;print warning and exit.
	call	cpm
	jp	wboot
dtst01	ld	c,12	;check CP/M version
	call	cpm
	ld	a,l	;make sure 2.x
	and	0f0h
	cp	20h
	jr	z,dtst02
	ld	de,dtstz;not CP/M 2.x, print
	ld	c,9	;error message and exit
	call	cpm
	jp	wboot
dtst02	xor	a,a	;initialize variables
	ld	(bypass),a
	ld	(skew),a
	ld	(list),a
	ld	(lockio),a
	ld	(restor),a
	ld	(lockpt),a
	ld	(pass),a
	ld	(pass+1),a
	ld	(errors),a
	ld	(errors+1),a
			;now set up test
			;configuration
	ld	de,dtstb
	call	getyn	;itemize errors?
	cp	'y'
	jr	z,dtst03;yes
	ld	a,1	;no
	ld	(bypass),a
	jr	dtst04	;skip query for output
			;device, since errors
			;will not be listed
dtst03	ld	de,dtstc;audit errors on console
	call	getl	;or line printer?
	cp	'c'
	jr	z,dtst04;c=use console 
	cp	'p'
	call	nz,query
	jr	nz,dtst03;no match,try again
	ld	a,1	;p=use line printer
	ld	(list),a
dtst04	ld	de,dtstd;lock on read or write?
	call	getl
	cp	'n'	;n=no locks
	jr	z,dtst06
	cp	'r'	;r=lock on read
	jr	nz,dtst05
	ld	a,1
	ld	(lockio),a
	jr	dtst12	;bypass querys about
			;restore mode and
			;data pattern: since
			;we are locked in read
			;mode, they are
			;irrelevant.
			;
dtst05	cp	'w'	;w=lock on write
	call	nz,query
	jr	nz,dtst04 ;no match,try again
	ld	a,2
	ld	(lockio),a
	jr	dtst08	;bypass restore question,
			;since we are locked in
			;write mode.
			;
dtst06	ld	de,dtste;restore user data?
	call	getyn
	cp	'y'	;y=restore
	jr	nz,dtst08 
	ld	a,1	;n=do not restore
	ld	(restor),a
dtst08	ld	de,dtstf;lock on data pattern?
	call	getyn
	cp	'n'
	jr	z,dtst12;n=use variable pattern
	ld	a,1	;y=lock on pattern
	ld	(lockpt),a ;supplied by operator
	ld	de,dtstg;accept data pattern
	call	geth	;from keyboard
	ld	(pattrn),a
dtst12	ld	de,dtsth;select drive to be
	call	getl	;used for test
	sub	'a'	;convert to logical #
	cp	$drvf	;make sure its legal
	call	c,query
	jr	c,dtst12 ;too small,try again
	cp	$drvl+1
	call	nc,query
	jr	nc,dtst12 ;too large,try again
	ld	(drv),a	;save drive assignment
	add	'A'	;also format for output
	ld	(dtsti1),a
	ld	de,dtsti;confirm selected drive?
	call	getyn
	cp	'n'
	jr	z,dtst12;not confirmed,try again
			;
			;initialize track
			;limits
	ld	hl,$trkf
	ld	(trkf),hl
	ld	hl,$trkl
	ld	(trkl),hl
dtst15	ld	de,dtstj ;test all tracks?
	call	getyn
	cp	'y'	;y=use all of them
	jr	z,dtst20;n=user wants to specify
			;  range of tracks
dtst17	ld	de,dtstk;enter first track
	call	getn	;to test
	ld	(trkf),hl ;save it
	ld	de,dtstl;enter last track
	call	getn	;to test
	ld	(trkl),hl ;save it
	ld	de,(trkf);make sure first
	or	a,a	;track<=last track
	sbc	hl,de
	call	c,query	;wrong,start over
	jr	c,dtst17
dtst20			;initialize sector
			;limits
	ld	hl,$secf
	ld	(secf),hl
	ld	hl,$secl
	ld	(secl),hl
dtst22	ld	de,dtstm;use all sectors
	call	getyn	;of each track?
	cp	'y'
	jr	z,dtst26;y=use all sectors
			;n=user wants to specify
			;range of sectors
dtst24	ld	de,dtstn;enter first sector
	call	getn	;to test.
	ld	(secf),hl ;save it.
	ld	de,dtsto;enter last sector
	call	getn	;to test.
	ld	(secl),hl ;save it.
	ld	de,(secf);make sure first
	or	a,a	;sector<=last sector
	sbc	hl,de
	call	c,query
	jr	c,dtst24;error,start over
			;
			;all variables set up
			;now --- how many
dtst26	ld	de,dtstp;test passes should be
	call	getn	;made?
	ld	(passl),hl ;save # of passes
			;
			;print advisory message
	ld	de,dtstt;as test begins
	ld	c,9
	call	cpm
	ld	de,dtstu;remind user whether he
	ld	a,(restor) ;is using restore
	or	a,a	;mode
	jr	z,dtst32
	ld	de,dtstv
dtst32	ld	c,9
	call	cpm
			;
dtst40			;begin a pass
	ld	hl,(trkf)
	ld	(trk),hl;initialize current track
			;
dtst42			;process next track
	ld	c,6	;check for interruption
	ld	e,0ffh
	call	cpm	;from console
	or	a,a
	jp	nz,dtst94 ;break detected,quit
	ld	a,(restor)
	or	a,a	;is this restore mode?
	jr	z,dtst45;no,jump
	ld	hl,buff3;yes, save current
	ld	de,merr1;disk contents
	call	rdbuf
dtst45	ld	a,(lockio)
	cp	a,1	;is this lock on read?
	jr	z,dtst47;yes,jump
	ld	hl,buff1;set up test pattern
	ld	de,$bpt	
	call	bufpat
	ld	hl,buff1;write test pattern
	ld	de,merr2
	call	wtbuf
dtst47	ld	a,(lockio)
	cp	a,2	;is this lock on write?
	jr	z,dtst70;yes,jump
	ld	hl,buff2;read back test pattern
			;(or just read existing
			; data if locked on read)
	ld	de,merr3
	call	rdbuf
dtst50	ld	a,(lockio)
	or	a,a	;is this lock on
			;read or write?
	jr	nz,dtst70 ;yes,jump
			;no, compare test data
	ld	hl,buff1;written to data read
	ld	de,buff2;back from disk. If
	ld	bc,merr4;difference found,
	call	bufcmp	;print error message
dtst70	ld	a,(restor)
	or	a,a	;using restore mode?
	jr	z,dtst80;no,jump
			;yes,write back user's
			;data
	ld	hl,buff3
	ld	de,merr6
	call	wtbuf
	ld	hl,buff1;verify that
	ld	de,merr7;it was rewritten ok
	call	rdbuf
	ld	hl,buff1
	ld	de,buff3
	ld	bc,merr5;check restored data
	call	bufcmp
			;if difference found,
			;print 'data cannot
			;be restored'
			;
dtst80			;advance current track
	ld	de,(trk)
	inc	de
	ld	(trk),de
	ld	hl,(trkl)
	or	a,a	;done with all tracks?
	sbc	hl,de
	jp	nc,dtst42 ;no,process another
			;
dtst90			;end of pass
	ld	bc,(pass)
	inc	bc	;count passes
	ld	(pass),bc
	ld	hl,dtstr1
	call	conv	;convert pass #
	ld	bc,(errors)
	ld	hl,dtstr2
	call	conv	;convert error count
	ld	de,dtstr;print pass and errors
	ld	c,9	;on console
	call	cpm
	ld	a,(list);also using printer?
	or	a,a
	jr	z,dtst92;no,jump
			;yes,also send pass
			;and error count to
			;list device
	ld	hl,dtstr
	call	perr9
dtst92			;reset error count
	xor	a,a
	ld	(errors),a
	ld	(errors+1),a
	ld	hl,(pass)
	ld	de,(passl)
	or	a,a	;are enough passes done?
	sbc	hl,de
	jp	c,dtst40;not yet,loop
dtst94			;done with all passes
	ld	de,dtstw;ask whether to exit
	call	getl	;or to continue test
	cp	'c'	;c=continue
	jp	z,dtst
	cp	'e'	;e=exit
	jr	nz,dtst94;if no match,try again
	ld	de,dtstx;print goodbye
	ld	c,9
	call	cpm	;and return control
	jp	wboot	;to CP/M
			;
	form
;
; routines to read and write up to one track
;
rdbuf			;read current track from
			;secf to secl
			;
			;call hl=buffer base addr
			;     de=error msg addr
	ld	(rdbufa),de ;save message address
	ld	(buffer),hl ;save buffer address
	ld	hl,0	;initialize transfer byte
	ld	(iolen),hl ;count
	call	seldsk	;select disk
	ld	hl,(secf)
	ld	(sec),hl;initialize current sector
rdbuf1	call	setio	;set up track,sector,memory
	call	read	;now request transfer
	or	a,a	;was i/o successful?
	jr	z,rdbuf2;no error,jump
	ld	de,(rdbufa)
	call	perr	;i/o error, audit it
rdbuf2	call	rwadv  	;advance sector address
	jr	nc,rdbuf1;not done,read another
	ret		;back to caller
rdbufa	dw	0	;address of error message
;
wtbuf			;write current track
			;from secf to secl
			;
			;call de=error msg addr
			;     hl=buffer base addr
			;
	ld	(wtbufa),de ;save message addr
	ld	(buffer),hl ;save memory addr
	ld	hl,0	;initialize transfer
	ld	(iolen),hl ;byte count
	call	seldsk	;select disk drive
	ld	hl,(secf)
	ld	(sec),hl;initialize current sector
wtbuf1	call	setio	;set track,sector,memory
	call	write	;request disk write
	or	a,a	;any i/o errors?
	jr	z,wtbuf2;no,jump
	ld	de,(wtbufa)
	call	perr	;error, audit it
wtbuf2	call	rwadv  	;advance sector address
	jr	nc,wtbuf1;not done,write another
	ret		;back to caller
wtbufa	equ	rdbufa	;save address of error
			;message
			;
rwadv			;advance sector and 
			;memory addresses
			;
	ld	de,$bps	; de <- bytes per sector
	ld	hl,(buffer)
	add	hl,de	;update buffer address
	ld	(buffer),hl
	ld	hl,(iolen)
	add	hl,de	;count bytes transferred
	ld	(iolen),hl
	ld	de,(sec)
	inc	de	;advance current sector
	ld	(sec),de
	ld	hl,(secl)
	or	a,a	;done with all sectors?
	sbc	hl,de	;exit with carry set if
			;done
	ret
	form
;
; set up buffer with test pattern
;
bufpat			;call hl=address of base
			;        of buffer
			;     de=byte length of
			;        area to set up
	ld	a,(lockpt)
	or	a,a	;are we locked on user
			;specified data pattern?
	jr	nz,bufpa2;yes,jump
bufpa1	ld	a,r	;read refresh register
	xor	a,h
	add	a,l
			;make data a function of
			;memory address
	ld	(hl),a	;and store it
	inc	hl	;advance buffer address
	dec	de	;count bytes stored
	ld	a,d	;done yet?
	or	a,e
	jr	nz,bufpa1 ;no,loop
	ret
			;user specified pattern
bufpa2	ld	a,(pattrn)
	ld	(hl),a 	;store one byte
	inc	hl	;advance buffer address
	dec	de	;count bytes stored
	ld	a,d	;done yet?
	or	a,e
	jr	nz,bufpa2 ;not done,loop
	ret		;exit
			;
	form
;
;
; compare specified buffer and print error
; message if difference found
;
bufcmp			;compare buffers
			;
			;call bc=address of
			;        error message
			;     de=address 1st buffer
			;     hl=address 2nd buffer
			;
	ld	(bufcma),bc ;save msg address
	ld	(bufcmb),hl ;save base of buffer
	ld	bc,(iolen)  ;length to compare
bufcm1	ld	a,(de)	;fetch byte from 1st buffer
	cp	a,(hl)	;compare it to 2nd buffer
	jr	nz,bufcm3 ;difference found,jump
bufcm2	inc	hl	;advance buffer addresses
	inc	de
	dec	bc	;count bytes
	ld	a,b	;done yet?
	or	a,c
	jr	nz,bufcm1 ;no,loop
	ret		;back to caller
			;
bufcm3			;difference found, print
			;error audit trail
	push	bc	;first save registers
	push	de
	push	hl
	ld	de,(bufcmb)
	or	a,a
	sbc	hl,de	;find a buffer offset
	push	hl	;now divide by bytes per
	pop	bc	;sector to find relative
	ld	de,$bps	;sector number
	call	div
	ld	hl,(secf)
	add	hl,bc	;add relative sector to
			;first sector to find
			;actual address for use
			;by PERR
	ld	(sec),hl
	ld	de,(bufcma)
	call	perr	;now audit error
	pop	hl	;restore registers
	pop	de
	pop	bc
bufcm4			;advance memory address
			;out of this sector where
			;an error was found.
	inc	hl	;bump buffer addresses
	inc	de
	dec	bc	;done with all data area?
	ld	a,b
	or	a,c
	ret	z	;yes,exit compare routine
	ld	a,l	;check if on new sector  
	and	$bps-1	;boundary
	jr	z,bufcm1;found it, go compare
			;more data
	jr	bufcm4	;keep advancing until
			;sector boundary.
			;
bufcma	dw	0	;address of error message
bufcmb	dw	0	;base buffer address
	form
; 
perr			;error printing routine,
			;prints pass,drive,track,
			;sector, and message
			;specified by caller on
			;console or list device.
			;
			; call with de=address
			;   of message giving
			;   type of error
			;
	ld	a,(bypass)
	or	a,a	;is error itemization
			;bypass flag set?
	jr	nz,perr2;yes,skip printing
			;and go count errors
	ld	(perra),de ;save message addr.
	ld	bc,(pass)
	inc	bc
	ld	hl,perrc;convert current pass
	call	conv
	ld	a,(drv) ;form drive name
	add	'A'
	ld	(perrd),a
	ld	bc,(trk);convert current track
	ld	hl,perre
	call	conv
	ld	bc,(sec);convert current sector
	ld	a,(skew);is skew in effect?
	or	a,a
	jr	z,perr0	;no
	call	sectran	;yes, translate sector
perr0	ld	hl,perrf
	call	conv
	ld	a,(list);should output be on
	or	a,a	;console or printer?
	jr	nz,perr3;jump,use printer
			;fall thru,use console
	ld	hl,(errors)
	ld	a,h	;is this first error?
	or	a,l
	jr	nz,perr1;no,jump
	ld	de,dtstq;print title for errors
	ld	c,9
	call	cpm
perr1	ld	de,perrb;print disk address
	ld	c,9
	call	cpm
	ld	de,(perra)
	ld	c,9	;print error type
	call	cpm
			;
perr2			;count errors
	ld	hl,(errors)
	inc	hl
	ld	(errors),hl
	ret		;back to caller
			;
perr3			;errors to printer
	ld	hl,(errors)
	ld	a,h	;is this 1st error to
	or	a,l	;be printed this pass?
	jr	nz,perr4;no,jump
	ld	hl,dtstq;yes,print title
	call	perr9
perr4	ld	hl,perrb;print disk address
	call	perr9
	ld	hl,(perra)
	call	perr9	;print error type
	jr	perr2	;go count errors
			;
perr9			;send a string 
			;terminated by '$'
			;to list device
	ld	a,(hl)	;fetch next char
	cp	a,'$'	;is it terminator?
	ret	z	;yes,exit
	push	hl	;save string addr.
	ld	e,a	;send this character
	ld	c,5
	call	cpm
	pop	hl	;restore string addr
	inc	hl	;and increment it
	jr	perr9	;check next char.
			;
perra	dw	0	;addr of message
			;describing error type
perrb	db	cr,lf
perrc	db	'nnnn    ' ;pass #
perrd	db	'n     '   ;drive
perre	db	'nnnn   '  ;track
perrf	db	'nnnn   $' ;sector
;
	form
;
;
; disk interface to CP/M BIOS
;
seldsk	ld	a,(drv)	;select disk drive
	ld	c,a
	ld	de,24
			;this routine links
			;to the desired routine
			;through the standard
			;CP/M BIOS jump table
jpbios	ld	hl,(wboot+1)
	add	hl,de
	jp	(hl)
;
settrk	ld	bc,(trk);select track
	ld	de,27
	jr	jpbios
;
setsec	ld	bc,(sec);select sector
	ld	de,30
	ld	a,(skew);use sector skew?
	or	a,a
	jr	z,jpbios;no
	call	sectran ;translate sector addr.
	jr	jpbios
;
setdma	ld	bc,(buffer) ;set memory addr.
	ld	de,33
	jr	jpbios
;
setio	call	settrk	;set up track,sector,
	call	setsec	;and memory address
	call	setdma	;for subsequent read
	ret		;or write
;
read			;read one disk sector
	ld	de,36
	jr	jpbios
;
write			;write one disk sector
	ld	de,39
	jr	jpbios
;
sectran			;translate logical to
			;physical sector number
			;
			;call bc=logical sector
			;return bc=physical sector
	push	hl
	ld	hl,sectrb-1
	add	hl,bc
	ld	c,(hl)
	pop	hl
	ret
sectrb	db	1,4,7,10,13	;table built
	db	16,19,22,25,2	;with skew
	db	5,8,11,14,17,20	;factor =3
	db	23,26,3,6,9,12
	db	15,18,21,24
	form
;
; messages for test initialization and
; error printing
;
dtsta	db	cr,lf,lf
	db	'Z-80 Floppy Disk '
	db	'Test version '
	db	$ver+'0','.'
	db	$rev+'0',cr,lf
	db	'(c) 1980 Laboratory '
	db	'Microsystems',cr,lf,'$'
dtstb	db	cr,lf,'Itemize '
	db	'errors?    $'
dtstc	db	cr,lf,'Use '
	db	'console or printer'
	db	'? (C/P) $'
dtstd	db	cr,lf,'Lock on read '
	db	'or write? (N/R/W) $'
dtste	db	cr,lf,'Restore '
	db	'original data? $'
dtstf	db	cr,lf,'Lock on '
	db	'data pattern? $'
dtstg	db	cr,lf,'Enter data '
	db	'pattern, hex 00-FF$'
dtsth	db	cr,lf,'Drive '
	db	'to be tested '
	db	'(',$drvf+'A','-'
	db	$drvl+'A',') $'
dtsti	db	cr,lf,'Confirm: test drive '
dtsti1	db	'X ? $'
dtstj	db	cr,lf,'Test all '
	db	'tracks?    $'
dtstk	db	cr,lf,'First '
	db	'track to test      $'
dtstl	db	cr,lf,'Last '
	db	'track to test      $'
dtstm	db	cr,lf,'Test all '
	db	'sectors?    $'
dtstn	db	cr,lf,'First '
	db	'sector to test    $'
dtsto	db	cr,lf,'Last '
	db	'sector to test     $'
dtstp	db	cr,lf,'How many '
	db	'test passes?    $'
dtstq	db	cr,lf,lf,'Pass  '
	db	'Drive  Track  '
	db	'Sector  Error-type'
	db	cr,lf,'$'
dtstr	db	cr,lf,lf,'Pass '
dtstr1	db	'nnnn complete, '
dtstr2	db	'nnnn errors.'
	db	cr,lf,'$'
dtsts	db	cr,lf,'Not enough '
	db	'memory to execute.'
	db	cr,lf,'$'
dtstt	db	cr,lf,lf,'Beginning '
	db	'disk test - push '
	db	'any key to abort '
	db	'program.',cr,lf,'$'
dtstu	db	'Warning: user '
	db	'data will not be '
	db	'restored.',cr,lf,'$'
dtstv	db	'User data will be '
	db	'restored.',cr,lf,'$'
dtstw	db	cr,lf,'Continue or '
	db	'exit test? (C/E)$'
dtstx	db	cr,lf,lf
	db	'Goodbye.',cr,lf,'$'
dtsty	db	cr,lf,'Use sector '
	db	'skew?  $'
dtstz	db	cr,lf,'Need CP/M 2.x '
	db	'to execute.',cr,lf,'$'
;
;
merr1	db	'read error - original data$'
merr2	db	'write error - test pattern$'
merr3	db	'read error - test pattern$'
merr4	db	'compare error - test pattern$'
merr5	db	'original data cannot '
	db	'be restored$'
merr6	db	'write error - restore phase$'
merr7	db	'read error - restore phase$'
;
	form
;
;
; utility and console input routines
;
getyn			;get y or n response
			;from operator.
			;
			;call de=address of cue
			;return acc=y or n
	push	de	;save cue address
	ld	c,9	;print cue message
	call	cpm
	ld	de,getyna
	ld	c,9	;print possible answers
	call	cpm
	call	getchar	;get a character
			;from console
	or	a,20h	;fold to lower case
	pop	de	;restore cue address
			;in case needed again
	cp	'y'	;make sure response
			;is ok
	ret	z	;exit if y
	cp	'n'
	ret	z	;exit if n
	push	de
	call	query	;print question mark if
	pop	de	;not y or n, try again
	jr	getyn	;
getyna	db	'(Y/N) ',tab,'> $'
;
;
getl			;get any letter response
			;from operator.
			;
			;call de=address of cue
			;return acc=ASCII char.
	ld	c,9	;print cue message
	call	cpm
	ld	de,getla;tab and print 
	ld	c,9	;cue mark
	call	cpm
	call	getchar	;read console
	or	a,20h	;fold to lower case
	ret
getla	db	tab,'> $'
;
;
getn			;get a decimal number 
			;from the console.
			;
			;call de=address of cue
			;return hl=number
	push	de	;save cue message address
			;in case needed later
	ld	c,9
	call	cpm	;print cue message
	ld	de,getna;print tab and cue mark
	ld	c,9
	call	cpm
	ld	hl,0	;initialize forming
			;answer
	ld	a,(digits)
	ld	b,a	;total characters allowed
			;to be input
getn1	push	hl	;save answer
	push	bc	;save char. count
	call	getchar	;read console
	pop	bc	;restore char. count
	pop	hl	;restore forming answer
	cp	cr	;is this return?
	jr	z,getn9	;yes,exit with answer
	cp	'0'	;is this legal char.?
	jr	c,getn3	;no, jump
	cp	'9'+1	;is this legal char.?
	jr	nc,getn3;no,jump
	and	0fh	;isolate bottom 4 bits
			;previous data * 10
	push	hl
	pop	de
	add	hl,hl	;(*2)
	add	hl,hl	;(*4)
	add	hl,de	;(*5)
	add	hl,hl	;(*10)
	ld	e,a	;now add in this digit
	ld	d,0
	add	hl,de
	djnz	getn1	;count characters accepted
	jr	getn9	;enough accepted,exit
getn3			;illegal character detected.
	call	query	;print question mark and
	pop	de	;restart input
	jr	getn
getn9			;input complete,clean
			;stack and exit with
			;answer in (hl)
	pop	de
	ret
getna	db	tab,'> $'
getnb	db	'?$'
;
;
geth			;get $dig hex digits
			;from keyboard
			;
			;call de=addr of cue
			;return acc=lower 8 bits
			;     of entered number,
			;     hl=entire 16 bit no.
			;
	push	de	;save cue address
			;in case needed again
	ld	c,9
	call	cpm	;print cue message
	ld	de,getha;print tab and cue mark
	ld	c,9
	call	cpm
	ld	hl,0	;initialize forming
			;answer
	ld	a,(digits)
	ld	b,a	;max digits to accept
geth1	push	bc	;save registers
	push	hl
	call	getchar	;read console
	pop	hl
	pop	bc	;restore registers
	cp	cr	;if carriage return exit
	jr	z,geth25
	cp	'0'	;make sure its legal
	jr	c,geth3 ;no,jump
	cp	'9'+1	;if alpha fold to
	jr	c,geth15;lower case
	or	20h
geth15	cp	'f'+1	;make sure its legal
	jr	nc,geth3;no,jump
	cp	'a'	;check if alpha
	jr	c,geth2 ;jump if 0-9
	add	9	;add correction
geth2	and	0fh
	add	hl,hl	;previous data *16
	add	hl,hl	;(left shift 4 bits)
	add	hl,hl
	add	hl,hl
	add	a,l	;add this char. to
	ld	l,a	;forming result
	djnz	geth1	;keep reading console
geth25	pop	de	;clean up stack
	ld	a,l	;put lower 8 bits
			;of answer in acc.
			;(in case exit by
			; carriage return)
	ret
geth3	call	query   ;print question mark
	pop	de	;then restart input
	jr	geth
getha	db	tab,'> $'
;
;
query	push	af	;save flags
	ld	c,9	;print question mark
	ld	de,querya
	call	cpm
	pop	af	;restore flags
	ret
querya	db	' ?$'
;
;
getchar			;get 1 character from
			;console via raw input
			;mode.  do not echo a
			;carriage return.
	ld	e,0ffh
	ld	c,6
	call	cpm	;read console
	or	a,a	;anything there?
	jr	z,getchar ;no,try again
	cp	cr	;is it a carriage return?
	ret	z	;yes
	push	af	;no,echo it
	ld	e,a
	ld	c,6
	call	cpm
	pop	af	;restore acc. and exit
	ret
;
;
conv			;convert binary to
			;decimal ascii
			;
			;call bc=binary data, in
			;        range 0000-9999.
			;     hl=first byte addr
			;	 to store output
			;
	ld	de,1000
	call	div
	call	conv9	;thousands digit
	ld	de,100
	call	div
	call	conv9	;hundreds digit
	ld	de,10
	call	div
	call	conv9	;tens digit
	call	conv9	;units
	ret		;back to caller
conv9	ld	a,c	;turn quotient into
	add	'0'	;ASCII character
	ld	(hl),a	;and store it 
	inc	hl	;bump output pointer
	push	de	;bc <- remainder
	pop	bc
	ret
;
;
div			;single precision divide
			;call bc=numerator
			;     de=divisor
			;return bc=quotient
			;     de=remainder
	push	hl
	ld	hl,0
	or	a,a
	sbc	hl,de
	ex	de,hl
	ld	hl,0
	ld	a,17
div0	push	hl
	add	hl,de
	jr	nc,div1
	ex	(sp),hl
div1	pop	hl
	push	af
	rl	c
	rl	b
	rl	l
	rl	h
	pop	af
	dec	a
	jr	nz,div0
	or	a,a
	rr	h
	rr	l
	ex	de,hl
	pop	hl
	ret
;
;
buff1	equ	1000h	;disk buffers
buff2	equ	$bpt*2+buff1
buff3	equ	$bpt*2+buff2
buffend	equ	$bpt*2+buff3
;
;
	end	100h
